#pragma once

#include "Resource.h"
#include <cassert>
#include <iostream>
#include "VertexBuffer.h"
#include "IndexBuffer.h"

#include <assimp.h>
#include <aiScene.h>
#include <aiPostProcess.h>

#include <glm\glm.hpp>

#include "Utils.h"

class MeshResource: public Resource
{
public:
	struct Submesh {
		
		unsigned int m_Index;
		MeshResource* m_Mesh;

		VertexBuffer* m_VertexBuffer;
		IndexBuffer* m_IndexBuffer;

		Submesh(): m_Index(0), m_Mesh(NULL), m_VertexBuffer(NULL), m_IndexBuffer(NULL) {}

		Submesh(unsigned int index, MeshResource* mesh, VertexBuffer* vb, IndexBuffer* ib):
			m_Index(index), m_Mesh(mesh), m_VertexBuffer(vb), m_IndexBuffer(ib) {}

		~Submesh() {
			delete m_VertexBuffer;
			delete m_IndexBuffer;
		}

		size_t getDeviceSize() {
			return m_VertexBuffer->getDeviceSize() + m_IndexBuffer->getDeviceSize();
		}

	};

protected:
	bool m_Static;

	Submesh* m_Submeshes;
	unsigned long m_SubmeshCount;

	size_t m_Size;

public:

	MeshResource(void): m_Submeshes(NULL), m_SubmeshCount(0), m_Size(0), m_Static(false) {}

	virtual ~MeshResource(void) {
		Unload();
	}

	size_t GetSize() { 
		return m_Size; 
	}

	unsigned long GetSubmeshCount() { return m_SubmeshCount; }
	
	void setStatic(bool staticMesh) { 

		if(staticMesh != IsStatic() && !IsEmpty()) {
			std::cout << "WARNING: Setting mesh static setting when it's already loaded." << std::endl;
			Unload();
			m_Static = staticMesh;
			Reload();
		}
		else {
			m_Static = staticMesh;
		}

	}

	const Submesh* getSubmeshes() const {
		return m_Submeshes;
	}

	bool IsStatic() { return m_Static; }

	virtual bool Load(wstring filePath) {
		Resource::Load(filePath);

		string path = WStringToString(filePath);
		const aiScene* scene = aiImportFile(path.c_str(), aiProcess_Triangulate | aiProcess_SortByPType);
		
		if(scene == NULL) return false;

		m_SubmeshCount = scene->mNumMeshes;
		m_Submeshes = new Submesh[m_SubmeshCount];

		for(int i=0; i<scene->mNumMeshes; ++i) {
			
			const aiMesh* mesh = scene->mMeshes[i];
			Submesh* submesh = m_Submeshes + mesh->mMaterialIndex;
			submesh->m_Index = mesh->mMaterialIndex;
			submesh->m_Mesh = this;

			VertexData* vertexData = new VertexData[mesh->mNumVertices];
			for(int vi=0; vi<mesh->mNumVertices; ++vi) {
				
				VertexData* v = vertexData+vi;
				aiVector3D p = mesh->mVertices[vi];
				aiVector3D n = mesh->mNormals[vi];
				aiVector3D uv = mesh->mTextureCoords[vi][0];

				v->position.x = p.x;
				v->position.y = p.y;
				v->position.z = p.z;
				v->normal.x = n.x;
				v->normal.y = n.y;
				v->normal.z = n.z;
				v->uvCoordinates.s = uv.x;
				v->uvCoordinates.t = uv.y;
				
			}

			submesh->m_VertexBuffer = new VertexBuffer(vertexData, mesh->mNumVertices, IsStatic() ? 0 : DYNAMIC);

			index_t* indices = new index_t[mesh->mNumFaces];
			index_t currentIndex = 0;
			for(int fi=0; fi<mesh->mNumVertices; ++fi) {
				aiFace face = mesh->mFaces[fi];
				if(face.mNumIndices != 3) continue;

				indices[currentIndex++] = face.mIndices[0];
				indices[currentIndex++] = face.mIndices[1];
				indices[currentIndex++] = face.mIndices[2];				
			}

			int triangleCount = currentIndex;

			submesh->m_IndexBuffer = new IndexBuffer(indices, triangleCount, IsStatic() ? 0 : DYNAMIC);
		}

		aiReleaseImport(scene);

		return true;
	}

	virtual void Unload() {
		if(m_Submeshes != NULL)
			delete[] m_Submeshes;

		Resource::Unload();
	}

	virtual bool Reload() {
		return Load(m_FilePath);
	}

	virtual bool IsEmpty() { return m_Submeshes == NULL; }
	
};
